package com.zb.excel.importExcel.listener;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zb.constant.NetworkConstant;
import com.zb.constant.RedisImportDataConstant;
import com.zb.entity.*;
import com.zb.excel.NetworkEmpImport;
import com.zb.excel.NetworkImport;
import com.zb.redis.RedisUtil;
import com.zb.secure.BCryptUtil;
import com.zb.service.*;
import com.zb.utils.RandomUtil;
import com.zb.utils.TransactionUtil;
import com.zb.utils.VerificUtil;
import com.zb.utils.ZbExcelFieldUtil;
import com.zb.websocket.server.ProgressWebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @Author WEN
 * @Date 2024/2/27 0027
 **/
public class NetworkImportListener implements ReadListener<NetworkImport> {
    private static final Logger logger = LoggerFactory.getLogger(NetworkImportListener.class);
    private static int BATCH_COUNT = 300; // 每批次执行插入数据库数量
    private boolean isFirstSend; // 是否首次发送进度
    private boolean isTwoSend; // 是否二次发送进度
    private boolean isEndSend; // 是否最后一次发送进度
    private int excelTotalRowCount; // Excel总数量（此包含空行的数量）
    private int totalCount; // 导入的总数量
    private int batch_no; // 导入的批次号
    private long currentExcuteCount; // 当前执行的数量
    // 校验数据重复的缓存key
    private String check_data_repeat_key;
    private String CACHE_IMPORT_DATA_REDIS_KEY;
    private Map<String, Object> dataRepeatMap;
    private boolean isSendMessage; // 是否已发送消息
    private String progressUuid; // 进度uuid识别码
    private AccounLoginInfo loginInfo;
    private List<NetworkImport> importFailt;
    private StringBuffer message;

    private List<ZjNetworkType> networkTypeList;
    private List<ZjNetwork> addNetworkList;
    private List<ZjNetwork> updateNetworkList;
    private List<ZjNetworkStrategy> networkStrategyList;
    private List<ZjNetworkPath> networkPathList;
    private List<ZjAccount> addAccountList;
    private List<ZjAccount> updateAccountList;
    private List<ZjNetworkEmp> networkEmpList;
    private List<ZjNetworkEmpRelation> addNetworkEmpRelationList;
    private List<ZjNetworkEmpRelation> updateNetworkEmpRelationList;

    private PlatformTransactionManager transactionManager;
    private ZjNetworkService networkService;
    private ZjNetworkStrategyService networkStrategyService;
    private ZjNetworkPathService networkPathService;
    private ZjNetworkTypeService networkTypeService;
    private ZjAccountService accountService;
    private ZjNetworkEmpService networkEmpService;
    private ZjNetworkEmpRelationService networkEmpRelationService;

    public NetworkImportListener(String progressUuid, AccounLoginInfo loginInfo, PlatformTransactionManager transactionManager, ZjNetworkService networkService,
                                 ZjNetworkStrategyService networkStrategyService, ZjNetworkPathService networkPathService, ZjNetworkTypeService networkTypeService,
                                 ZjAccountService accountService, ZjNetworkEmpService networkEmpService, ZjNetworkEmpRelationService networkEmpRelationService) {
        this.isFirstSend = true;
        this.isTwoSend = true;
        this.isEndSend = false;
        this.excelTotalRowCount = 0;
        this.totalCount = 0;
        this.batch_no = 0;
        this.currentExcuteCount = 0;
        this.isSendMessage = false;
        this.importFailt = new ArrayList<>();
        this.message = new StringBuffer();

        this.loginInfo = loginInfo;
        this.progressUuid = progressUuid;
        this.check_data_repeat_key = RedisImportDataConstant.EXCEL + "check-network-data-repeat:" + loginInfo.getCustAccId() + ":" + this.progressUuid;
        this.CACHE_IMPORT_DATA_REDIS_KEY = RedisImportDataConstant.NETWORK_IMPORT + loginInfo.getCustAccId() + this.progressUuid;

        this.transactionManager = transactionManager;
        this.networkService = networkService;
        this.networkStrategyService = networkStrategyService;
        this.networkPathService = networkPathService;
        this.networkTypeService = networkTypeService;
        this.accountService = accountService;
        this.networkEmpService = networkEmpService;
        this.networkEmpRelationService = networkEmpRelationService;
        init();
    }

    private void init() {
        this.networkTypeList = new ArrayList<>();
        this.addNetworkList = new ArrayList<>();
        this.updateNetworkList = new ArrayList<>();
        this.networkStrategyList = new ArrayList<>();
        this.networkPathList = new ArrayList<>();
        this.addAccountList = new ArrayList<>();
        this.updateAccountList = new ArrayList<>();
        this.networkEmpList = new ArrayList<>();
        this.addNetworkEmpRelationList = new ArrayList<>();
        this.updateNetworkEmpRelationList = new ArrayList<>();
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        this.excelTotalRowCount = context.readSheetHolder().getApproximateTotalRowNumber();
        this.totalCount = this.excelTotalRowCount -3;
        if (this.isFirstSend) {
            this.message.append("数据正在解析中，请稍等...").append("</br>");
            ProgressWebSocketServer.send(this.progressUuid, false,  new ArrayList(), this.message.toString());
            this.isFirstSend = false;
        }
    }

    @Override
    public void invoke(NetworkImport networkImport, AnalysisContext analysisContext) {
        if (this.isTwoSend) {
            this.message.append("数据解析成功，正在导入中..").append("</br>");
            ProgressWebSocketServer.send(this.progressUuid, false,  new ArrayList(), this.message.toString());
            this.isTwoSend = false;
        }
        this.currentExcuteCount++;
        analysisContext.readWorkbookHolder().setIgnoreEmptyRow(false);
        int rowNo = analysisContext.readRowHolder().getRowIndex() + 1; // Excel行号
        if (ZbExcelFieldUtil.isLineNullValue(networkImport)) {
            this.totalCount--;
            return;
        }
        if (checkEmpty(rowNo, networkImport)) {
            sendMessage();
            return;
        }
        if (checkDataRepeat(rowNo, networkImport)) {
            sendMessage();
            return;
        }
        Map<String, Object> mapKey = getCacheData();
        // 服务商分类
        ZjNetworkType networkType = null;
        if (null != mapKey.get("network_type_" + networkImport.getNetworkType())) {
            networkType = JSON.parseObject(JSON.toJSONString(mapKey.get("network_type_" + networkImport.getNetworkType())), ZjNetworkType.class);
        } else {
            networkType = new ZjNetworkType();
            networkType.setId(IdWorker.getId());
            networkType.setCustAccId(this.loginInfo.getCustAccId());
            networkType.setName(networkImport.getNetworkType());
            networkType.setDisabled(false);
            this.networkTypeList.add(networkType);
            mapKey.put("network_type_" + networkImport.getNetworkType(), networkType);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 服务商
        ZjNetwork network = null;
        ZjNetworkStrategy strategy = null;
        ZjNetworkPath networkPath = null;
        if (null != mapKey.get("network_" + networkImport.getName())) {
            network = JSON.parseObject(JSON.toJSONString(mapKey.get("network_" + networkImport.getName())), ZjNetwork.class);
            network.setName(networkImport.getName());
            network.setTeamType(networkImport.getTeamType());
            network.setNetworkTypeId(networkType.getId());
            network.setProvince(networkImport.getProvince());
            network.setCity(networkImport.getCity());
            network.setArea(networkImport.getArea());
            network.setTown("");
            network.setAddress(networkImport.getAddress());
            this.updateNetworkList.add(network);
        } else {
            network = new ZjNetwork();
            network.setId(IdWorker.getId());
            network.setCustAccId(loginInfo.getCustAccId());
            network.setCode(RandomUtil.random10());
            network.setName(networkImport.getName());
            network.setNetworkTypeId(networkType.getId());
            network.setProvince(networkImport.getProvince());
            network.setCity(networkImport.getCity());
            network.setArea(networkImport.getArea());
            network.setTown("");
            network.setAddress(networkImport.getAddress());
            network.setCategory(networkImport.getCategory());
            network.setType(0);
            network.setTeamType(networkImport.getTeamType());
            network.setHasAloneTeamChangeApply(false);
            network.setTeamAuthenStatus(NetworkConstant.NETWORK_TEAM_OR_ALONE_AUTHEN_STATUS_1);
            network.setAloneAuthenStatus(NetworkConstant.NETWORK_TEAM_OR_ALONE_AUTHEN_STATUS_1);
            network.setDisabled(false);
            network.setCreateEmpId(loginInfo.getId());
            network.setCreateEmp(loginInfo.getName());
            network.setUpdateEmpId(loginInfo.getId());
            network.setUpdateEmp(loginInfo.getName());
            this.addNetworkList.add(network);

            strategy = new ZjNetworkStrategy();
            strategy.setId(network.getId());
            strategy.setCustAccId(loginInfo.getCustAccId());
            strategy.setAutoCheckCancelOrder(false);
            strategy.setAutoCheckEndOrder(false);
            strategy.setEnableUpdateSparePrice(false);
            strategy.setAutoSureReceiveOrder(true);
            strategy.setWithdrawHandlingFeeStrategy(1);
            strategy.setHandingFeeProportion(new BigDecimal("0.0"));
            strategy.setHandingFeeFixedOrder(new BigDecimal("0.0"));
            this.networkStrategyList.add(strategy);
            if (networkImport.getCategory() == 2) {
                networkPath.setId(network.getId());
                networkPath = new ZjNetworkPath();
                networkPath.setCustAccId(loginInfo.getCustAccId());
                networkPath.setPath("");
                networkPath.setPublicKey("");
                this.networkPathList.add(networkPath);
            }
        }
        // 服务商账号
        ZjAccount account = null;
        if (null != mapKey.get("network_emp_account_"+ networkImport.getPhone())) {
            account = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_account_" + networkImport.getPhone())), ZjAccount.class);
            account.setName(networkImport.getUsername());
            this.updateAccountList.add(account);
        } else {
            account = new ZjAccount();
            account.setId(IdWorker.getId());
            account.setSalt(BCryptUtil.generatorSalt());
            account.setAccount("ZB_" + BCryptUtil.generatorSalt().substring(10, 20));
            account.setPhone(networkImport.getPhone());
            account.setPassword(BCryptUtil.encryPassword(BCryptUtil.MD5(networkImport.getPhone().substring(5)), account.getSalt()));
            account.setName(networkImport.getUsername());
            account.setNickname(networkImport.getPhone().substring(6));
            account.setAvatar("");
            account.setStatus(0);
            this.addAccountList.add(account);
            mapKey.put("network_emp_account_" + networkImport.getPhone(), account);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 工程师人员
        ZjNetworkEmp networkEmp = null;
        if (null != mapKey.get("network_emp_" + account.getId())) {
            networkEmp = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_" + account.getId())), ZjNetworkEmp.class);
        } else {
            networkEmp = new ZjNetworkEmp();
            networkEmp.setId(IdWorker.getId());
            networkEmp.setAutoSureReceiveOrder(true);
            networkEmp.setAccountId(account.getId());
            this.networkEmpList.add(networkEmp);
            mapKey.put("network_emp_" + account.getId(), networkEmp);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 关联服务商
        ZjNetworkEmpRelation networkEmpRelation = null;
        if (null == mapKey.get("network_emp_relation_" + network.getId() + networkEmp.getId())) {
            networkEmpRelation = new ZjNetworkEmpRelation();
            networkEmpRelation.setId(IdWorker.getId());
            networkEmpRelation.setCustAccId(loginInfo.getCustAccId());
            networkEmpRelation.setNetworkId(network.getId());
            networkEmpRelation.setNetworkEmpId(networkEmp.getId());
            networkEmpRelation.setSuperManager(true);
            networkEmpRelation.setManager(true);
            networkEmpRelation.setQuit(false);
            this.addNetworkEmpRelationList.add(networkEmpRelation);
            mapKey.put("network_emp_relation_" + network.getId() + networkEmp.getId(), networkEmpRelation);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        } else {
            networkEmpRelation = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_relation_" + network.getId() + networkEmp.getId())), ZjNetworkEmpRelation.class);
            networkEmpRelation.setNetworkEmpId(networkEmp.getId());
            this.updateNetworkEmpRelationList.add(networkEmpRelation);
        }
        if (addNetworkList.size() >= BATCH_COUNT || updateNetworkList.size() >= BATCH_COUNT) {
            excuteSave();
        }
        sendMessage();
    }

    private Map<String, Object> getCacheData() {
        Map<String, Object> cacheData = null;
        Object byKey = RedisUtil.getByKey(this.CACHE_IMPORT_DATA_REDIS_KEY);
        if (null == byKey) {
            return new HashMap<>();
        }
        cacheData = JSON.parseObject((String) byKey, Map.class);
        return cacheData;
    }

    private boolean checkDataRepeat(int rowNo, NetworkImport networkImport) {
        boolean isDataRepeat = false;
        Object byKey = RedisUtil.getByKey(check_data_repeat_key);
        dataRepeatMap = JSON.parseObject((String) byKey, HashMap.class);
        if (null == dataRepeatMap) {
            dataRepeatMap = new HashMap<>();
        }
        // 服务商名称
        if (null != dataRepeatMap.get("network_" + networkImport.getName())) {
            this.message.append("A").append(rowNo).append("单元 服务商名称已重复填写").append("</br>");
            isDataRepeat = true;
        } else {
            dataRepeatMap.put("network_" + networkImport.getName(), true);
        }
        // 负责人手机号
        if (null != dataRepeatMap.get("network_emp_account_" + networkImport.getPhone())) {
            this.message.append("E").append(rowNo).append("单元 工程师手机号已重复填写").append("</br>");
            isDataRepeat = true;
        } else {
            dataRepeatMap.put("network_emp_account_" + networkImport.getPhone(), true);
        }
        RedisUtil.setCache(check_data_repeat_key, JSON.toJSONString(dataRepeatMap));
        if (isDataRepeat) {
            this.importFailt.add(networkImport);
        }
        return isDataRepeat;
    }

    private boolean checkEmpty(int rowNo, NetworkImport networkImport) {
        if (StringUtils.isBlank(networkImport.getName())) {
            this.importFailt.add(networkImport);
            message.append("A").append(rowNo).append("单元 服务商名称不能为空").append("</br>");
            return true;
        }
        if (networkImport.getName().length() > 200) {
            this.importFailt.add(networkImport);
            message.append("A").append(rowNo).append("单元 服务商名称长度限制200个字符以内").append("</br>");
            return true;
        }
        if (null == networkImport.getTeamType()) {
            this.importFailt.add(networkImport);
            message.append("B").append(rowNo).append("单元 团队模式不能为空").append("</br>");
            return true;
        }
        if (!Pattern.matches("^(1|2)$", networkImport.getTeamType().toString())) {
            this.importFailt.add(networkImport);
            message.append("B").append(rowNo).append("单元 团队模式参数不合法").append("</br>");
            return true;
        }
        if (null == networkImport.getCategory()) {
            this.importFailt.add(networkImport);
            message.append("C").append(rowNo).append("单元 服务商模式不能为空").append("</br>");
            return true;
        }
        if (!Pattern.matches("^(1|2)$", networkImport.getTeamType().toString())) {
            this.importFailt.add(networkImport);
            message.append("C").append(rowNo).append("单元 服务商模式参数不合法").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(networkImport.getUsername())) {
            this.importFailt.add(networkImport);
            message.append("D").append(rowNo).append("单元 负责人名称不能为空").append("</br>");
            return true;
        }
        if (networkImport.getUsername().length() > 100) {
            this.importFailt.add(networkImport);
            message.append("D").append(rowNo).append("单元 负责人名称长度限制100个字符以内").append("</br>");
            return true;
        }
        if (!VerificUtil.phoneVerific(networkImport.getPhone())) {
            this.importFailt.add(networkImport);
            message.append("E").append(rowNo).append("单元 负责人手机号格式错误").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(networkImport.getNetworkType())) {
            this.importFailt.add(networkImport);
            message.append("F").append(rowNo).append("单元 服务商分类名称不能为空").append("</br>");
            return true;
        }
        if (networkImport.getNetworkType().length() > 100) {
            this.importFailt.add(networkImport);
            message.append("F").append(rowNo).append("单元 服务商分类名称长度限制100个字符以内").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(networkImport.getProvince())) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-省不能为空").append("</br>");
            return true;
        }
        if (networkImport.getProvince().length() > 50) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-省长度限制50个字符以内").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(networkImport.getCity())) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-市不能为空").append("</br>");
            return true;
        }
        if (networkImport.getCity().length() > 50) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-市长度限制50个字符以内").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(networkImport.getArea())) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-区/县不能为空").append("</br>");
            return true;
        }
        if (networkImport.getArea().length() > 50) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-区/县长度限制50个字符以内").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(networkImport.getAddress())) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-详情地址不能为空").append("</br>");
            return true;
        }
        if (networkImport.getAddress().length() > 50) {
            this.importFailt.add(networkImport);
            message.append("G").append(rowNo).append("单元 服务商地址-详情地址长度限制50个字符以内").append("</br>");
            return true;
        }
        return false;
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        RedisUtil.clearCache(check_data_repeat_key);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        RedisUtil.clearCache(check_data_repeat_key);
        if (CollectionUtil.isNotEmpty(addNetworkList) || CollectionUtil.isNotEmpty(updateNetworkList)) {
            excuteSave();
        }
        if (!this.isEndSend) {
            ProgressWebSocketServer.send(this.progressUuid, true, 100, this.totalCount, this.importFailt, this.message.toString());
            this.isEndSend = true;
        }
    }

    private void excuteSave() {
        String color = "#67C23A";
        this.batch_no++;
        this.message.append("<div style=color:").append(color).append(">解析完成，正在处理保存 [第 ").append(this.batch_no).append("批] 数据，请耐心等待...</div>");
        sendMessage();
        this.message.append("<div style=color:").append(color).append(">第 ").append(this.batch_no);
        TransactionStatus status = this.transactionManager.getTransaction(TransactionUtil.transactionDefinition());
        try {
            if (CollectionUtil.isNotEmpty(networkTypeList)) this.networkTypeService.saveBatch(networkTypeList);
            if (CollectionUtil.isNotEmpty(addNetworkList)) this.networkService.saveBatch(addNetworkList);
            if (CollectionUtil.isNotEmpty(updateNetworkList)) this.networkService.updateBatchById(updateNetworkList);
            if (CollectionUtil.isNotEmpty(networkStrategyList)) this.networkStrategyService.saveBatch(networkStrategyList);
            if (CollectionUtil.isNotEmpty(networkPathList)) this.networkPathService.saveBatch(networkPathList);
            if (CollectionUtil.isNotEmpty(addAccountList)) this.accountService.saveBatch(addAccountList);
            if (CollectionUtil.isNotEmpty(updateAccountList)) this.accountService.updateBatchById(updateAccountList);
            if (CollectionUtil.isNotEmpty(networkEmpList)) this.networkEmpService.saveBatch(networkEmpList);
            if (CollectionUtil.isNotEmpty(addNetworkEmpRelationList)) this.networkEmpRelationService.saveBatch(addNetworkEmpRelationList);
            if (CollectionUtil.isNotEmpty(updateNetworkEmpRelationList)) this.networkEmpRelationService.updateBatchById(updateNetworkEmpRelationList);
            this.transactionManager.commit(status);
            this.message.append(" 批导入成功</div>");
        }catch (Exception e) {
            e.printStackTrace();
            color = "#F56C6C";
            logger.error("服务商数据第 {} 批导入失败，失败原因：{}", this.batch_no, e.getMessage());
            this.message.append(" 导入失败！原因：").append(e.getMessage()).append("</div>");
            this.transactionManager.rollback(status);
        }
        init();
    }

    /**
     * 发送进度消息
     */
    private void sendMessage() {
        ProgressWebSocketServer.send(this.progressUuid, false, (1.0 * this.currentExcuteCount / this.excelTotalRowCount *100), this.importFailt, this.message.toString());
    }
}
